/*
 * Copyright (C) 2011-2013 Eric Lasota
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#ifndef __RDX_OBJECTMANAGEMENT_HPP__
#define __RDX_OBJECTMANAGEMENT_HPP__

#include <string.h>

#include "rdx_utility.hpp"
#include "rdx_gcslots.hpp"

// Load staging:
// A type can be laid out if:
//  - The parent class, if any, is laid out
//  - All properties of struct/enum type are laid out
// Step 1: Symbols of core types (ones with GCOF_Core) are loaded.
// Step 2: Default instances are loaded

// Concurrency stuff:
// - RDX only follows OS concurrency rules.  It never reorders reads or writes, but does not stop CPU reordering.
//   Host-controlled threads are possible with ExecuteTimeSlice
//
//   It is possible to isolate objects using domains.  Objects can only access global symbols in compatible domains
//   and the host controls domain visibility.  The runtime does not enforce that objects only reference symbols
//   in properly-visible domains, but violations may cause problems with serialization.
//
// Global operations execute when only one thread (the one that caused the global operation) is the only active one.
// If another thread attempts to initiate a global operation, it will block until the global operation completes.
// Global operations are always safe and must never attempt to block.
//
// Lock process:
// - Acquire global sync status check mutex (blocks thread activation)
//   Check the synchronization flags to see if the operation can be run (including global sync, even if global sync isn't needed to run)
//   If a flag is in use:
//      If blocked globally, decrement active thread counter
//      Release check mutex
//          If blocked globally:
//              Acquire global sync mutex
//              Increment active thread counter
//              Release global sync mutex
//      Repeat lock attempt
//   If all are available:
//      Mark desired flags as unavailable
//      Acquire applicable mutex
//      Release check mutex
//      If a global operation, spin until the active thread counter hits 1
//      Perform operations
//      Release applicable mutex
//
// Global operations:
// - Serialization writes
// - Garbage collection
//
// Mutually exclusive operations (non-global):
// - Allocation operations
// - String lookups
// - GST operations and serialization reads
//
// - The mutex process is as follows:
//   Check volatile global sync flag
//   - If enabled:
//       Disable
//
// - IncRef/DecRef cause interlocks
//
//
// - Three serialization GCLs can be active at once: Garbage collector, package reserializer,
//   and package deserializer.  Package serialization and deserialization can happen at the same time:
//   If the garbage collector triggers during package load, it may cause a package to auto-reserialize.
//   Outbound serializations must be allocation-free.

namespace RDX
{
	struct OperationContext;
	
	template<class _Tvalue> class StaticLookupPODKey;
	template<class _Tkey, class _TlookupType> class StaticLookupTable;

	namespace Programmability
	{
		struct Type;
		class String;
		struct Enumerant;
		struct ArrayOfType;
		struct DelegateType;
		struct Property;
		struct ICodeProvider;
		struct StructuredType;
		struct RuntimeThread;
	};

	namespace IO
	{
		struct IFileStream;
		struct ITextDeserializer;
	};

	namespace Threading
	{
		struct IMutex;
	};

	namespace ObjectManagement
	{
		struct IObjectManager;
		struct ISerializer;
		struct ITypeProcessor;
		struct Package;
		struct GCInfo;
		struct ITypeSerializer;
		struct INativeTypeHost;
		struct Allocator;

		template<class _GCT>
		class CRef;

		typedef StaticLookupTable<StaticLookupPODKey<const GCInfo *>, LargeInt> SSIDTable;

		enum Domain
		{
			DOMAIN_Invalid = 0,
			DOMAIN_Indeterminate,	// Passed to DomainForSymbolName if loading a package
			
			DOMAIN_Duplicable,
			DOMAIN_Core,
			DOMAIN_Runtime,

			DOMAIN_Custom,

			DOMAIN_Maximum = RDX_MAXIMUM_DOMAIN_VALUE,
		};

		enum DomainStatePreservation
		{
			DSP_NeverCollect,	// Package will never be collected
			DSP_Save,			// Package contents will be written to disk if collected
			DSP_Volatile,		// Don't care
		};

		struct TargetableObject
		{
			enum TargetType
			{
				TT_Shell,		// This is the default type, which should prevent uninitialized shells from being traced
				TT_GCObject,

				// NOTE: Shells must NEVER be orphaned if a collectable reference points to them!  They must also never be inserted into the GST.
				// - Text package cataloging.  Protected by being in typeless hash tables.
				// - Package symbol loading.  Protected by being part of the package, and packages are always referenced with the shell.
			};

			TargetType targetType;
		};

		struct ObjectSerializationInfo
		{
			LargeInt serializeCatalogOffset;
			LargeInt serializeDataOffset;
		};

		struct GCInfo : public TargetableObject
		{
			GCInfo *prev[GCL_Count];
			GCInfo *next[GCL_Count];

			IObjectManager *ownerObjectManager;

			const Programmability::Type *containerType;					// Runtime type of the container, could be an ArrayOfType or a StructuredType
			const Programmability::Type *contentsType;					// Type of the contents, either the subtype for arrays or structured type for single objects
			const Programmability::StructuredType *containedStructure;	// Data type of the contents, NULL if the contents are a reference type, the structure if it's a structured type
			Programmability::Method **vft;								// Equivalent to containerType->virtualMethods.  Reduces an indirect.
			const ITypeProcessor *typeProcessor;							// Custom processor grapher for this object (usually pulled from containedStructure)
			
#ifdef RDX_ENABLE_DEBUG_GST_SYMBOL
			const Char *debugGSTSymbol;
#endif
			const Programmability::String *gstSymbol;
			Package *package;

			mutable UInt32 gcflags;

			enum Flags
			{
				//GCOF_Immutable				= 1,
				GCOF_Duplicable					= 2,		// Can be duplicated, even if not in DOMAIN_Duplicable.  Does not cause inclusion in packages.
				//GCOF_Atomic					= 4,		// Ignores base class
				GCOF_LinkTerminator				= 8,		// GCL terminator
				//GCOF_Placeholder				= 16,		// Placeholder initialized by the library, will be replaced if deserialized
				//GCOF_StartupValue				= 32,		// Startup value initialized in the GST
				//GCOF_NeverSerialize			= 64,		// Never serialize
				GCOF_Core						= 128,		// Built-in core type
				GCOF_Critical					= 256,		// Built-in type, loaded in a separate phase and may only contain references to other critical types and Object
				GCOF_Shelled					= 512,		// GCL_Shell
				GCOF_Unprocessed				= 1024,		// GCL_PendingProcess
				GCOF_UnloadedPackage			= 2048,		// GCL_UnloadedPackage
				GCOF_ReferencesMayBeShells		= 4096,		//
				GCOF_TransientDuplicate			= 8192,		// Transient duplicate used for validation, shouldn't be subjected to instance-specific verification

				GCOF_Test						= 32768,	// For debugging only
				GCOF_External					= 65536,	// Object is in a package not in the current serialize scan set
				GCOF_ConstantArray				= 262144,	// Is a constant array
				GCOF_ConstantStructure			= 524288,	// Is a constant structure resource
			};

			GCInfo();
			void Unlink(int gcl);
			void LinkBefore(GCInfo *nnext, int gcl);
			void LinkAfter(GCInfo *nprev, int gcl);
			void AddRefLink(CRef<void> *cref) const;
			void RemoveRefLink(CRef<void> *cref) const;

			LargeInt *dimensions;	// Located after the data
			LargeInt numElements;
			LargeInt elementSize;

			LargeInt numDimensions;
			
#ifdef RDX_ENABLE_SENTINELS
			UInt32 sequentialID;
			UInt32 *sentinel2;
			UInt32 sentinel1;
#endif
			
			Domain domain;
			mutable ScanID serializeScanID;

			// This must be the last property
			RDX_ALIGN_ATTRIBUTE(RDX_MAX_ALIGNMENT) UInt8 _objectData;

			GCInfo(IObjectManager *ownerObjectManager, const Programmability::Type *containerType, const Programmability::Type *contentsType,
				const Programmability::StructuredType *structureType, const ITypeProcessor *tp, LargeInt numElements, LargeInt elementSize, Int numDimensions);
			static LargeInt AllocationSize(LargeInt numElements, LargeInt elementSize, Int numDimensions);
			static const GCInfo *From(const void *ptr);
			static GCInfo *From(void *ptr);
		};
		
		// Chained reference
		// These should NEVER be contained by a collected type, use an ITypeProcessor and graph the references instead
		template<class _GCT>
		class CRef
		{
		protected:
			CRef<void> *_prev;
			CRef<void> *_next;
			_GCT *_obj;

		public:
			inline CRef()
			{
				_obj = NULL;
				_prev = NULL;
				_next = NULL;
			}

			inline CRef(_GCT *obj, bool incRef)
			{
				_obj = const_cast<void *>(static_cast<const void *>(obj));
				_prev = NULL;
				_next = NULL;
				if(obj && incRef)
					GCInfo::From(obj)->AddRefLink(this);
			}

			inline CRef(_GCT *obj)
			{
				_obj = obj;
				_prev = NULL;
				_next = NULL;
				if(obj)
					GCInfo::From(obj)->AddRefLink(this->Untype());
			}

			inline void Unlink(CRef<void> **head)
			{
				if(_prev)
					_prev->_next = _next;
				if(_next)
					_next->_prev = _prev;
				if(*head == this)
					*head = _next;
				_next = NULL;
				_prev = NULL;
				_obj = NULL;
			}

			inline void Link(_GCT *obj, CRef<void> **head)
			{
				_obj = obj;

				if(*head)
					(*head)->_prev = this;
				this->_next = *head;
				this->_prev = NULL;

				*head = this;
			}

			inline CRef<void> *Next()
			{
				return _next;
			}

			inline const CRef<void> *Next() const
			{
				return _next;
			}

			inline _GCT *Object() const
			{
				return _obj;
			}

			inline CRef<void> *Untype()
			{
				return reinterpret_cast<CRef<void>*>(this);
			}

			inline CRef<void> *Untype() const
			{
				return reinterpret_cast<const CRef<void>*>(this);
			}

			inline CRef<_GCT>& operator =(_GCT *rs)
			{
				if(rs == NULL && _obj != NULL)
					GCInfo::From(_obj)->RemoveRefLink(this->Untype());
				else if(rs != NULL && _obj == NULL)
				{
					_obj = rs;
					GCInfo::From(rs)->AddRefLink(this->Untype());
				}
				else
					_obj = rs;

				return *this;
			}

			inline CRef<_GCT>& operator =(CRef<_GCT>& rs)
			{
				if(rs._obj == NULL && _obj != NULL)
					GCInfo::From(_obj)->RemoveRefLink(this->Untype());
				else if(rs._obj != NULL && _obj == NULL)
				{
					_obj = rs._obj;
					GCInfo::From(rs._obj)->AddRefLink(this->Untype());
				}
				else
					_obj = rs._obj;

				return *this;
			}

			inline CRef(const CRef<_GCT> &rs)
			{
				_obj = rs._obj;
				_prev = NULL;
				_next = NULL;
				if(_obj)
					GCInfo::From(_obj)->AddRefLink(this->Untype());
			}

			inline ~CRef()
			{
				// IMPORTANT: Blocks from this need to occur BEFORE the reference is unlinked to ensure that values transfered to not-yet-traceable areas
				// such as return values become traceable before another blocking operation occurs.
				if(_obj)
					GCInfo::From(_obj)->RemoveRefLink(this->Untype());
			}

			inline _GCT * operator -> ()
			{
				return static_cast<_GCT*>(_obj);
			}

			inline operator const _GCT *() const
			{
				return static_cast<const _GCT*>(_obj);
			}

			inline operator _GCT *()
			{
				return static_cast<_GCT*>(_obj);
			}

			template<class _NT>
			inline CRef<_NT> Cast()
			{
				return static_cast<_NT*>(static_cast<_GCT*>(_obj));
			}

			template<class _NT>
			inline CRef<_NT> ReinterpretCast()
			{
				return CRef<_NT>(reinterpret_cast<_NT*>(_obj));
			}
		};

		struct UnloadedObject : public TargetableObject
		{
			void *resolution;

			inline UnloadedObject()
			{
				this->targetType = TT_Shell;
				resolution = NULL;
			}

			template<class _T>
			inline _T *InterpretAs()
			{
				return reinterpret_cast<_T*>(&reinterpret_cast<GCInfo*>(this)->_objectData);
			}
		};

		struct LoadShell
		{
			LargeInt						fileOffset;
			bool							isAnonymous;
			bool							isConstant;
			const Programmability::String	*typeName;
			LargeInt						tableIndex;
			UnloadedObject					*ref;
		};
		
		struct BuiltIns
		{
			CRef<Programmability::StructuredType> st_ArrayOfType;
			CRef<Programmability::StructuredType> st_DelegateType;
			CRef<Programmability::StructuredType> st_Object;
			CRef<Programmability::StructuredType> st_Thread;
			CRef<Programmability::StructuredType> st_Array;
			CRef<Programmability::StructuredType> st_Type;
			CRef<Programmability::StructuredType> st_Bool;
			CRef<Programmability::StructuredType> st_LargeInt;
			CRef<Programmability::StructuredType> st_Int;
			CRef<Programmability::StructuredType> st_Byte;
			CRef<Programmability::StructuredType> st_Char;
			CRef<Programmability::StructuredType> st_Varying;
			CRef<Programmability::StructuredType> st_String;
			CRef<Programmability::StructuredType> st_MethodParameter;
			CRef<Programmability::StructuredType> st_Property;
			CRef<Programmability::StructuredType> st_Enumerant;
			CRef<Programmability::StructuredType> st_StorageSpecifier;
			CRef<Programmability::StructuredType> st_StructuredType;
			CRef<Programmability::StructuredType> st_Method;
			CRef<Programmability::StructuredType> st_InstructionFileInfo;
			CRef<Programmability::StructuredType> st_InterfaceImplementation;
			CRef<Programmability::StructuredType> st_MutableObjectRef;

			CRef<Programmability::Enumerant> e_StorageSpecifierEnumerants;
			
			CRef<Programmability::ArrayOfType> aot_MethodParameter;
			CRef<Programmability::ArrayOfType> aot_Type;
			CRef<Programmability::ArrayOfType> aot_StructuredType;
			CRef<Programmability::ArrayOfType> aot_Instruction;
			CRef<Programmability::ArrayOfType> aot_Enumerant;
			CRef<Programmability::ArrayOfType> aot_Property;
			CRef<Programmability::ArrayOfType> aot_Method;
			CRef<Programmability::ArrayOfType> aot_InterfaceImplementation;
			CRef<Programmability::ArrayOfType> aot_Object;
			CRef<Programmability::ArrayOfType> aot_String;
			CRef<Programmability::ArrayOfType> aot_Int;
			CRef<Programmability::ArrayOfType> aot_Char;
			CRef<Programmability::ArrayOfType> aot_Byte;
			CRef<Programmability::ArrayOfType> aot_InstructionFileInfo;

			CRef<Programmability::Property> p_MethodProperties;
			CRef<Programmability::Property> p_EnumerantProperties;
			CRef<Programmability::Property> p_MethodParameterProperties;
			CRef<Programmability::Property> p_PropertyProperties;
			CRef<Programmability::Property> p_StructuredTypeProperties;
			CRef<Programmability::Property> p_ArrayOfTypeProperties;
			CRef<Programmability::Property> p_DelegateTypeProperties;
			CRef<Programmability::Property> p_InterfaceImplementationProperties;
			CRef<Programmability::Property> p_MutableObjectRefProperties;
			CRef<Programmability::Property> p_InstructionFileInfoProperties;

			// ======================================================================
			// These are only guaranteed to be set after a load
			CRef<void*> providerDictionary;
			LargeInt providerDictionaryTracedSymbolCount;
		};

		struct IPackageHost
		{
			virtual Domain DomainForSymbolName(IObjectManager *om, const Programmability::String *str, Domain domain = DOMAIN_Indeterminate) = 0;
			virtual IO::IFileStream *StreamForDomain(IObjectManager *om, Domain domain, bool write, bool &isText) = 0;
			virtual bool DomainsVisible(Domain sourceDomain, Domain destDomain) = 0;
			virtual bool DomainCanContainMethods(Domain domain) = 0;
		};

		struct Allocator
		{
			void *opaque;
			void *(*reallocFunc) (void *opaque, void *ptr, LargeInt sz, LargeInt align);

			inline void *Realloc(void *ptr, LargeInt size, LargeInt align) const { return this->reallocFunc(this->opaque, ptr, size, align); }

			template<class T>
			inline T* Realloc(T* ptr, LargeInt count) const { return reinterpret_cast<T*>(this->reallocFunc(this->opaque, ptr, count * static_cast<LargeInt>(sizeof(T)), RDX_ALIGNOF(T))); }

			template<class T>
			inline T* CAlloc(LargeInt count) const { return reinterpret_cast<T*>(this->reallocFunc(this->opaque, NULL, count * static_cast<LargeInt>(sizeof(T)), RDX_ALIGNOF(T))); }

			inline void Free(void *ptr) const { this->reallocFunc(this->opaque, ptr, 0, 1); }
		};

		struct IObjectManager
		{
			enum ManagerOperationContext
			{
				MOC_Deserialize,
				MOC_Initialize,
			};
			
			virtual void *GetLocalResourcesMap() = 0;
			virtual void *GetImportedResourcesMap() = 0;

			virtual void Shutdown() = 0;

			virtual const Programmability::String *DeserializeBinaryString(OperationContext *ctx, IO::IFileStream *reader, Package *pkg) = 0;
			virtual const Programmability::String *DeserializeTextString(OperationContext *ctx, IO::ITextDeserializer *td) = 0;
			virtual void SerializeBinaryString(const Programmability::String *str, IO::IFileStream *fs, const ObjectManagement::SSIDTable *ssidTable) const = 0;
			virtual void SerializeTextString(const Programmability::String *str, IO::IFileStream *fs) const = 0;

			virtual void IncrementThreadCounter() = 0;
			virtual void DecrementThreadCounter() = 0;
			virtual Threading::IMutex *GetCoreMutex(int mNum) = 0;
			virtual void AddPersistentObject(CRef<void> *cref, void *obj) = 0;
			virtual void RemovePersistentObject(CRef<void> *cref) = 0;
			
			virtual const INativeTypeHost *GetTypeHost() const = 0;
			virtual const Programmability::ICodeProvider *GetCodeProvider() const = 0;
			virtual Allocator *GetAllocator() = 0;

			virtual void CollectGarbage(OperationContext *ctx) = 0;
			virtual void SetILDisposal(bool shouldDispose) = 0;
			virtual void SetCollectionFrequency(NonAtomicInt frequency) = 0;
			virtual void StartSerialize(ISerializer *ser, GCLink gcl) = 0;
			virtual void SerializeIncludeObject(ISerializer *ser, const void *obj, GCLink gcl) = 0;
			virtual void SerializeIncludeStructure(ISerializer *ser, const Programmability::StructuredType *st, const void *obj, GCLink gcl) = 0;
			virtual CRef<void> CreateContainer(OperationContext *ctx, LargeInt elementSize, LargeInt numElements, Int numDimensions, Domain domain, const Programmability::Type *t, const ITypeProcessor *tp, LargeInt overflow) = 0;
			virtual CRef<const Programmability::String> CreateString(OperationContext *ctx, const Char *str, LargeInt numChars = -1, bool lookupOnly = false) = 0;
			virtual CRef<const Programmability::String> CreateStringASCII(OperationContext *ctx, const char *str, LargeInt size = -1, bool lookupOnly = false) = 0;
			virtual CRef<const Programmability::String> CreateStringUTF8(OperationContext *ctx, const Byte *str, LargeInt size = -1, bool lookupOnly = false) = 0;
			virtual CRef<const Programmability::String> CreateStringConcatenated(OperationContext *ctx, const Programmability::String *str1, const Programmability::String *str2) = 0;
			virtual CRef<const Programmability::String> CreateStringSub(OperationContext *ctx, const Programmability::String *str, LargeInt startIndex, LargeInt length = -1) = 0;
			
			virtual CRef<Programmability::RuntimeThread> CreateThread(OperationContext *ctx, LargeInt stackSize) = 0;
			
			virtual CRef<void> LookupSymbolSimple(OperationContext *ctx, const Programmability::String *symbolName) = 0;		// Never fails, never excepts
			virtual CRef<void> LookupSymbol(OperationContext *ctx, const Programmability::String *symbolName, IPackageHost *packageHost) = 0;

			virtual CRef<Programmability::ArrayOfType> CreateArrayType(OperationContext *ctx, Programmability::Type *t, LargeInt numDimensions, bool constant) = 0;

			virtual void AddGlobalSymbol(OperationContext *ctx, const Programmability::String *symbolName, void *object) = 0;
			virtual void AddUnloadedObject(OperationContext *ctx, void *object) = 0;

			virtual BuiltIns *GetBuiltIns() = 0;
			virtual const BuiltIns *GetBuiltIns() const = 0;

			virtual void *ResolveShellableReference(void *ref) const = 0;
			virtual const void *ResolveShellableReference(const void *ref) const = 0;

			virtual CRef<Package> LoadPackage(OperationContext *ctx, Domain domain, IPackageHost *packageHost) = 0;
			virtual CRef<void> LoadObject(OperationContext *ctx, IPackageHost *packageHost) = 0;
			virtual void SavePackage(OperationContext *ctx, Domain domain, IPackageHost *packageHost) = 0;
			virtual void SaveObject(OperationContext *ctx, const void *object, IO::IFileStream *fs, bool isText) = 0;
			virtual void RegisterPackage(OperationContext *ctx, Domain domain, Package *pkg) = 0;
			virtual bool PackageLoaded(Domain domain) = 0;
			
			virtual bool ObjectCompatible(const void *from, const Programmability::Type *to) const = 0;
			virtual bool EnumCompatible(EnumValue ev, const Programmability::Enumerant *enums) const = 0;
			virtual bool TypesCompatible(const Programmability::Type *from, const Programmability::Type *to) const = 0;
			virtual bool TypesCompatiblePolymorphic(const Programmability::Type *from, const Programmability::Type *to) const = 0;
			virtual bool TypeIsObjectReference(const Programmability::Type *t) const = 0;
			virtual bool TypeCanBeTraced(const Programmability::Type *t) const = 0;
			virtual bool TypeIsValid(const Programmability::Type *t) const = 0;
			virtual bool ObjectIsConstant(const void *v) const = 0;
			virtual void TypeValueSize(OperationContext *ctx, const Programmability::Type *t, LargeInt &size, LargeInt &align) const = 0;
			virtual LargeInt TypeElementSize(const Programmability::Type *t) = 0;

			virtual void AuditSentinels() = 0;

			virtual GCInfo *FirstLiveObject() = 0;
			virtual GCInfo *NextLiveObject(GCInfo *gci) = 0;

			virtual void DebugDumpGST() = 0;

			template<class _T>
			inline CRef<_T> CreateObject(OperationContext *ctx, const Programmability::StructuredType *t = NULL, const ITypeProcessor *tp = NULL, Domain domain = DOMAIN_Runtime)
			{
				CRef<void> container = this->CreateContainer(ctx, sizeof(_T), 1, 0, domain, t, tp, 0);
				return container.Cast<_T>();
			}

			template<class _T>
			CRef<_T> Create1DArray(OperationContext *ctx, LargeInt count, const Programmability::ArrayOfType *t = NULL, const ITypeProcessor *tp = NULL, Domain domain = DOMAIN_Runtime, LargeInt overflow = 0);

			inline CRef<void> CreateContainer(OperationContext *ctx, LargeInt elementSize, LargeInt numElements, Int numDimensions, Domain domain = DOMAIN_Runtime, LargeInt overflow = 0)
			{
				return CreateContainer(ctx, elementSize, numElements, numDimensions, domain, NULL, NULL, overflow);
			}
		};

		struct Package
		{
			struct ManifestImport
			{
				UnloadedObject					ref;		// MUST ALWAYS BE FIRST
				const Programmability::String	*symbolName;
				Domain							domain;
			};

			struct ManifestLocal
			{
				enum
				{
					PMLF_Exported		= 1,			// Visible to other packages
					PMLF_Critical		= 2,			// Must be loaded before anything else
					PMLF_Duplicate		= 4,			// Already exists in the GST, only verify identity
					PMLF_Anonymous		= 8,			// Don't insert in GST even if this has a name
					PMLF_Constant		= 16,			// References are ConstPointer
				};

				UnloadedObject					ref;			// MUST ALWAYS BE FIRST
				UnloadedObject					*typeRef;
				Programmability::Type			*resolvedType;
				const Programmability::String	*tempTypeName;
				const Programmability::String	*symbolName;
				UInt32							flags;
				Domain							domain;

				LargeInt						fileOffset;
			};

			enum Persistence
			{
				PERS_Collectable,	// Package contains only unmodifiable resources and may be collected (default)
				PERS_Persistent,	// Package persists for the life of the application
			};

			struct NativeProperties
			{
				Persistence						persistence;
				Domain							domain;
				ManifestImport					*importedSymbols;
				ManifestLocal					*localSymbols;
				const Programmability::String	**strings;
				bool							trusted;
				void							*firstObject;		// First object, kept for packages in DOMAIN_Runtime
			} _native;
		};

		IObjectManager *CreateObjectManager(Allocator alloc, const INativeTypeHost *nth, const Programmability::ICodeProvider *cp);

		CRef<Package> DeserializePackage(OperationContext *ctx, IObjectManager *objManager, Domain domain,
			IPackageHost *host, IO::IFileStream *stream, bool isText);
		bool DeserializeObject(OperationContext *ctx, IObjectManager *objManager, Package *pkg, Package::ManifestLocal *mfl,
			Domain domain, IPackageHost *host, IO::IFileStream *stream, bool isText);

		
		void WriteHeader(IObjectManager *objm, IO::IFileStream *fs, bool isText, LargeInt numLocalSymbols, LargeInt numImports, LargeInt numStrings);
		void WriteImport(OperationContext *ctx, IObjectManager *objm, IO::IFileStream *fs, bool isText, const SSIDTable *ssidTable, const Programmability::String *symbolName);
		void ReserveObjectExport(IObjectManager *objm, IO::IFileStream *fs, bool isText, const SSIDTable *ssidTable, void *obj);
		void WriteObjectExport(IObjectManager *objm, IO::IFileStream *fs, bool isText, LargeInt *catalogOffset, LargeInt objectOffset, const void *obj);
		void WriteObject(IObjectManager *objm, IO::IFileStream *fs, bool isText, const SSIDTable *ssidTable, const void *obj);
		void WriteValue(IObjectManager *objm, IO::IFileStream *fs, bool isText, const SSIDTable *ssidTable, const Programmability::Type *t, const void *obj);
		
		void DeserializeValue(OperationContext *ctx, IObjectManager *objManager, Package *pkg,
			Domain domain, IPackageHost *host, IO::IFileStream *fs, bool isText, const Programmability::Type *t, void *propertyLocation, int depth);
	}
};

#include "rdx_longflow.hpp"
#include "rdx_programmability.hpp"
#include "rdx_threading.hpp"

namespace RDX
{
	namespace ObjectManagement
	{
		inline GCInfo::GCInfo()
		{
		}

		inline void GCInfo::Unlink(int gcl)
		{
			if(prev[gcl])
				prev[gcl]->next[gcl] = next[gcl];
			if(next[gcl])
				next[gcl]->prev[gcl] = prev[gcl];
			next[gcl] = prev[gcl] = NULL;
		}

		inline void GCInfo::LinkBefore(GCInfo *nnext, int gcl)
		{
			if(nnext)
			{
				if(nnext->prev[gcl])
					prev[gcl] = nnext->prev[gcl];
				nnext->prev[gcl]->next[gcl] = this;
				nnext->prev[gcl] = this;
				next[gcl] = nnext;
			}
			else
				next[gcl] = NULL;
		}

		inline void GCInfo::LinkAfter(GCInfo *nprev, int gcl)
		{
			if(nprev)
			{
				if(nprev->next[gcl])
					next[gcl] = nprev->next[gcl];
				nprev->next[gcl]->prev[gcl] = this;
				nprev->next[gcl] = this;
				prev[gcl] = nprev;
			}
			else
				prev[gcl] = NULL;
		}

		inline GCInfo::GCInfo(IObjectManager *ownerObjectManager, const Programmability::Type *containerType, const Programmability::Type *contentsType,
			const Programmability::StructuredType *structureType, const ITypeProcessor *tp, LargeInt numElements, LargeInt elementSize, Int numDimensions)
		{
			this->containerType = containerType;
			this->contentsType = contentsType;
			this->containedStructure = structureType;
			this->typeProcessor = tp;
			this->ownerObjectManager = ownerObjectManager;
			this->gcflags = 0;
			this->gstSymbol = NULL;
			this->targetType = TT_GCObject;
			this->package = NULL;
#ifdef RDX_ENABLE_DEBUG_GST_SYMBOL
			this->debugGSTSymbol = NULL;
#endif

			if(structureType && !tp)
				this->typeProcessor = structureType->_native.user.typeProcessor;

			this->numDimensions = numDimensions;
			this->elementSize = elementSize;
			this->numElements = numElements;

			this->dimensions = reinterpret_cast<LargeInt*>(reinterpret_cast<char*>(this)
					+ AllocationSize(numElements, elementSize, 0));
		}

		inline LargeInt GCInfo::AllocationSize(LargeInt numElements, LargeInt elementSize, Int numDimensions)
		{
			LargeInt totalSpace = RDX_OFFSETOF(GCInfo, _objectData);

			if(!Utility::CheckMulOverflow(elementSize, numElements)) return 0;
			LargeInt dataSpace = elementSize * numElements;
			if(!Utility::CheckAddOverflow(totalSpace, dataSpace)) return 0;
			totalSpace += dataSpace;

			if(numDimensions > 0)
			{
				LargeInt paddingSpace = totalSpace % static_cast<LargeInt>(RDX_ALIGNOF(LargeInt));
				if(!Utility::CheckAddOverflow(totalSpace, paddingSpace)) return 0;
				totalSpace += paddingSpace;

				if(!Utility::CheckMulOverflow(sizeof(LargeInt), numDimensions)) return 0;
				LargeInt dimensionSpace = static_cast<LargeInt>(sizeof(LargeInt)) * static_cast<LargeInt>(numDimensions);
				if(!Utility::CheckAddOverflow(totalSpace, dimensionSpace)) return 0;
				totalSpace += dimensionSpace;
			}

			return totalSpace;
		}

		inline const GCInfo *GCInfo::From(const void *ptr)
		{
			if(ptr == NULL)
				return NULL;

			return reinterpret_cast<const GCInfo *>(reinterpret_cast<const UInt8*>(ptr) - RDX_OFFSETOF(GCInfo, _objectData));
		}

		inline GCInfo *GCInfo::From(void *ptr)
		{
			if(ptr == NULL)
				return NULL;

			return reinterpret_cast<GCInfo*>(reinterpret_cast<UInt8*>(ptr) - RDX_OFFSETOF(GCInfo, _objectData));
		}

		inline void GCInfo::RemoveRefLink(CRef<void> *ref) const
		{
			ownerObjectManager->RemovePersistentObject(ref);
		}

		inline void GCInfo::AddRefLink(CRef<void> *ref) const
		{
			ownerObjectManager->AddPersistentObject(ref, const_cast<UInt8 *>(&_objectData));
		}

		template<class _T>
		inline CRef<_T> IObjectManager::Create1DArray(OperationContext *ctx, LargeInt count, const Programmability::ArrayOfType *t, const ITypeProcessor *tp, Domain domain, LargeInt overflow)
		{
			RDX_TRY(ctx)
			{
				CRef<_T> container;
				RDX_PROTECT_ASSIGN(ctx, container, this->CreateContainer(ctx, sizeof(_T), count, 1, domain, t, tp, overflow).Cast<_T>());
				GCInfo::From(container)->dimensions[0] = count;
				return container;
			}
			RDX_CATCH(ctx)
			{
				RDX_RETHROWV(ctx, NULL);
			}
			RDX_ENDTRY
		}
	}
}


#endif
